/* Ty, a programming language interpreter
 * Copyright (C) 2007 Nick Thomas
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef TY_DATA_H
#define TY_DATA_H

#include <stdint.h>
#include <wchar.h>

/* The Ty heap consists of two-word chunks, which each hold a single object.
 * Since these chunks are aligned on four-byte boundaries (at least), we have
 * two bits in the pointer that can be used as type tags, which nicely fits
 * our four data types:
 * 
 *  0  Number
 *  1  String
 *  2  Cons
 *  3  Word
 *
 * So, Ty objects are passed around as tagged pointers (the ty_obj type), and
 * the actual data is stored on the heap. Chunks are represented by the
 * ty_chunk union.
 *
 * Numbers and conses are so trivial to represent that they do not bear further
 * comment. Strings are represented as their length and a pointer to the data.
 * Words are represented as a pointer to their name (a chunk containing a
 * string) and their definition (an object). The only tricky part is the
 * location of the magic bit. Since we know that the name is a string, we can
 * do away with its type tag, and then we have two free bits. The first of
 * these is the magic bit, and the second is unused.
 *
 * For magic words, the pointer to the magic is stored in the definition
 * field. This pointer points to a type field of type ty_magic_tag, indicating
 * the type of magic. The rest of the magic may be located beyond this tag.
 */

#define TY_TAG_NUMBER 0
#define TY_TAG_STRING 1
#define TY_TAG_CONS 2
#define TY_TAG_WORD 3

/* Turns a ty_obj into a chunk pointer. */
#define ty_obj_chunk(obj) ((ty_chunk *)((obj) & ~3))

/* Turns a chunk pointer into a ty_obj. */
#define ty_make_obj(obj,type) ((ty_obj)((obj) | type))

/* Returns the type of a ty_obj. */
#define ty_obj_type(obj) ((obj) & 3)

typedef uintptr_t ty_obj;
/* XXX: What is the proper definition for this? */
typedef wchar_t ty_char;

typedef struct {
    uintptr_t length;
    ty_char *data;
} ty_string;

typedef struct {
    ty_obj car;
    ty_obj cdr;
} ty_cons;

#define ty_word_get_magic(word) ((uintptr_t)((word).name) & 1)
#define ty_word_set_magic(word) ((word).name |= 1)
#define ty_word_unset_magic(word) ((word).name &= ~1)
#define ty_word_get_name(word) ((word).name & ~1)
#define ty_word_set_name(word,newname) ((word).name = newname | \
                                        ty_word_get_magic(word))

typedef uintptr_t ty_magic_tag;

typedef struct {
    ty_string *name;
    ty_obj definition;
} ty_word;

typedef union ty_chunk {
    double number;
    ty_string string;
    ty_cons cons;
    ty_word word;
    /* Used by the garbage collector. */
    union ty_chunk *forward_ptr;
} ty_chunk;

#endif
